#include "video.hpp"
#include "disk.hpp"
#include "image.hpp"
#include "camera.hpp"

#include "grid.hpp"

#include "stage.hpp"
#include "cue.hpp"

#include "SDL.h"
#include "cJSON.h"

#include <map>
#include <string>
#include <sstream>

class AriaEd
{
public:
	AriaEd(); 

	int Run(int argc, char *args[]);

private:
	// stage info
	std::string stgname;

	// drawing/placing tool
	struct Brush
	{
		Brush();

		enum Cmd
		{
			NONE,
			PUT,
			ERASE,
			GRAB,
			LFACE,
			RFACE,
			REPLACE,
			RESIZE
		} cmd;

		Vec2i pos;
		Grid grid;
		int prop;
		Cue cue;
	}
		brush;

	// assets
	Image *tileimg;
	Image *fontimg;
	Image *propimg;
	Image *cueimg;
	Image *actimgs[Cue::CUECT];

	// text output
	static const int TEXTW = 40, TEXTH = 2;
	static const int INFOC = 0, INFOR = 0;
	static const int TILEC = 0, CUEC = 20;
	static const int LHELPC = 0, RHELPC = 20, HELPR = 1;
	Grid text;
	Uint32 msgtime;

	// other i/o
	std::map<SDLKey, int> keyprops;
	Vec2i look;
	float zoom;

	// which screen
	void (AriaEd::*screenf)();

	// general funcs
	bool Save();
	void BrushWinPos(int winx, int winy);
	void Zoom(float delta, float lim);
	void Pan(int dx, int dy);
	void Event(SDL_Event & event);
	void TextDraw();

	// stage edit
	void StageScreen();
	void StagePaint(Vec2i & oripos);
	void StageGrab(Vec2i & oripos);
	void StageBrushDraw();

	// tile palette
	void TileScreen();
	void TileGrab(Vec2i & oripos);

	// cue select
	void CueScreen();
};

#include <algorithm>
/*
const int WIN_SCALE = 2;
const int WINPIX_PER_UNIT = Vid.PIX_PER_UNIT * WIN_SCALE;
const int WIN_WIDTH = Vid.WIDTH * WINPIX_PER_UNIT;
const int WIN_HEIGHT = Vid.HEIGHT * WINPIX_PER_UNIT;
*/
AriaEd::Brush::Brush()
{	
	cmd = NONE;
	pos = Vec2i(0, 0);
	grid.Resize(1, 1);
	prop = -1;
	cue = Cue();
}

AriaEd::AriaEd() :
	text(TEXTW, TEXTH)
{
	look = Vec2i(0, 0);
	zoom = 1;
	brush = Brush();
	const SDLKey PROP_KEYS[Stage::PROPCT] =
		{
			SDLK_s,
			SDLK_u,
			SDLK_d,
			SDLK_w,
			SDLK_f
		};

	for(int i = 0; i < Stage::PROPCT; ++i)
	{
		keyprops[PROP_KEYS[i]] = i;
	}
}

bool AriaEd::Save()
{
	std::string cuesname(stgname);
	cuesname += "cue";
	bool success = Stg.Save(stgname);
	success = success && Cues.Save(cuesname);
	return success;
}

void AriaEd::BrushWinPos(int winx, int winy)
{
	brush.pos.x = winx / zoom / Vid.PIX_PER_UNIT;
	brush.pos.y = winy / zoom / Vid.PIX_PER_UNIT;
	brush.pos += look;
}

void AriaEd::Zoom(float delta, float lim)
{
	if (delta < 0)
	{
		if (zoom + delta < lim)
			return;
	}
	else if (zoom + delta > lim)
		return;

	zoom += delta;
/*
	Vec2i mpos;
	SDL_GetMouseState(&mpos.x, &mpos.y);
	BrushWinPos(mpos.x, mpos.y);
*/
	Vid.Zoom(zoom);
	look = brush.pos - Cam.HSize();
}

void AriaEd::Pan(int dx, int dy)
{
	brush.pos += Vec2i(dx, dy);
	look += Vec2i(dx, dy);
}

void AriaEd::Event(SDL_Event & event)
{
	std::map<SDLKey, int>::iterator keyprop;

	switch (event.type)
	{
	case SDL_QUIT:
		screenf = NULL;
		break;

	case SDL_VIDEORESIZE:
		Vid.WinResize(event.resize.w, event.resize.h, Vid.SCREWASPECT);
		Vid.ResizeToWin();
		Vid.Zoom(zoom);
		break;

	case SDL_MOUSEMOTION:
		BrushWinPos(event.motion.x, event.motion.y);
		break;

	case SDL_MOUSEBUTTONDOWN:
		switch (event.button.button)
		{
		case SDL_BUTTON_LEFT:
			switch (brush.cmd)
			{
			case Brush::REPLACE:
				Stg.Replace(brush.pos.x, brush.pos.y, 
						brush.grid);
				brush.cmd = brush.NONE;
				break;

			case Brush::RESIZE:
				Stg.Resize(brush.pos.x + 1, brush.pos.y + 1);
				Cues.Resize(brush.pos.x + 1, brush.pos.y + 1);
				brush.cmd = brush.NONE;
				break;

			case Brush::NONE:
				brush.cmd = brush.PUT;
				break;

			default:
				;
			}
			break;

		case SDL_BUTTON_RIGHT:
			switch (brush.cmd)
			{
			case Brush::REPLACE:
			case Brush::RESIZE:
				brush.cmd = brush.NONE;
				break;

			case Brush::NONE:
				brush.cmd = brush.GRAB;
				break;

			default:
				;
			}
			break;

		case SDL_BUTTON_WHEELUP:
			Zoom(.25f, 2);
			break;

		case SDL_BUTTON_WHEELDOWN:
			Zoom(-.25f, .25f);
			break;

		default:
			;
		}
		break;

	case SDL_MOUSEBUTTONUP:
		switch (event.button.button)
		{
		case SDL_BUTTON_LEFT:
			if (brush.cmd == brush.PUT)
				brush.cmd = brush.NONE;
			break;

		case SDL_BUTTON_RIGHT:
			if (brush.cmd == brush.GRAB)
				brush.cmd = brush.NONE;
			break;
				
		default:
			;
		}
		break;

	case SDL_KEYDOWN:
		switch (event.key.keysym.sym)
		{
		case SDLK_ESCAPE:
			if (brush.cmd == brush.RESIZE)
				brush.cmd = brush.NONE;
		case SDLK_BACKSPACE:
			brush.prop = -1;
			break;

		case SDLK_F2:
			Save();
			break;

		case SDLK_F3:
			if (brush.cmd == brush.NONE)
			{
				brush.cmd = brush.REPLACE;
				screenf = &AriaEd::StageScreen;
			}
			break;

		case SDLK_F4:
			if (event.key.keysym.mod & (KMOD_LALT | KMOD_RALT))
				screenf = NULL;
			else if (brush.cmd == brush.NONE)
			{
				brush.cmd = brush.RESIZE;
				screenf = &AriaEd::StageScreen;
			}
			break;

		case SDLK_F5:
			screenf = &AriaEd::StageScreen;
			break;

		case SDLK_F6:
			screenf = &AriaEd::TileScreen;
			break;

		case SDLK_F7:
			screenf = &AriaEd::CueScreen;
			break;

		case SDLK_F12:
			Stg.Close();
			Stg.Open(stgname);
			tileimg = AllImgs.Get(stgname);
			break;

		case SDLK_DELETE:
			if (brush.cmd == brush.NONE)
				brush.cmd = brush.ERASE;
			break;

		case SDLK_UP:
			if (event.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
				Pan(0, -Vid.HEIGHT);
			else
				Pan(0, -1);
			break;

		case SDLK_DOWN:
			if (event.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
				Pan(0, Vid.HEIGHT);
			else
				Pan(0, 1);
			break;

		case SDLK_LEFT:
			if (event.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
				Pan(-Vid.WIDTH, 0);
			else
				Pan(-1, 0);
			break;

		case SDLK_RIGHT:
			if (event.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
				Pan(Vid.WIDTH, 0);
			else
				Pan(1, 0);
			break;

		case SDLK_KP_PLUS:
		case SDLK_PLUS:
		case SDLK_EQUALS:
			Zoom(.25f, 2);
			break;

		case SDLK_KP_MINUS:
		case SDLK_MINUS:
			Zoom(-.25f, .25f);
			break;
			
		case SDLK_COMMA:
			brush.cmd = brush.LFACE;
			break;

		case SDLK_PERIOD:
			brush.cmd = brush.RFACE;
			break;

		default:
			keyprop = keyprops.find(event.key.keysym.sym);

			if(keyprop != keyprops.end())
				brush.prop = keyprop->second;
		}
		break;

	case SDL_KEYUP:
		switch (event.key.keysym.sym)
		{
		case SDLK_DELETE:
			if (brush.cmd == brush.ERASE)
				brush.cmd = brush.NONE;
			break;

		default:
			;
		}
		break;

	default:
		;
	}
}

void AriaEd::TextDraw()
{
	const float SCALE = 2;
	Vid.Look(0, 0);
	Vid.Pos(0, Cam.size.y - (SCALE * text.Rows() / zoom));
	Vid.Scale(SCALE / zoom, SCALE / zoom);
	Vid.TilesDraw(*fontimg, text.Data(), text.Cols(), text.Rows());
	Vid.Scale(1, 1);
}

#include <cstdio>

void AriaEd::CueScreen()
{
	look.x = 0;
	look.y = 0;

	const char *WIN_TITLE = "Cues";
	SDL_WM_SetCaption(WIN_TITLE, WIN_TITLE);

	Cue overcue;

	while (screenf == &AriaEd::CueScreen)
	{
		SDL_Event event;

		while (SDL_PollEvent(&event))
		{
			Event(event);
		}

		overcue.num = overcue.Dir() 
			* cueimg->TileAtPoint(brush.pos.x, brush.pos.y);

		switch (brush.cmd)
		{
		case Brush::LFACE:
		case Brush::RFACE:
			overcue.Face(brush.cmd == brush.LFACE ? -1 : 1);
			brush.cmd = brush.NONE;
			break;

		case Brush::GRAB:
			if (!overcue.IsValid())
				break;
			brush.cue = overcue;
			brush.cmd = brush.NONE;
			screenf = &AriaEd::StageScreen;
			break;

		default:
			;
		}

		Cam.Pos(look.x, look.y);
		Cam.Out();

		Vid.Clear();
		text.Clear();

		Vid.Flip(overcue.Dir() < 0, false);

		for (float y = 0; 
		     y < cueimg->Height();
		     y += cueimg->TileHeight())
		{
			for (float x = 0; 
			     x < cueimg->Width();
			     x += cueimg->TileWidth())
			{
				int tile = cueimg->TileAtPoint(x, y);
					
				Vid.Pos(x, y);
				Vid.SpriteDraw(*cueimg, tile);
			}
		}

		text.Printf(RHELPC, HELPR, L"R: Grab cue");
		TextDraw();

		Vid.DrawEnd();
	}
}

void AriaEd::TileGrab(Vec2i & oripos)
{
	if (oripos.x < 0 || oripos.y < 0)
	{
		oripos = brush.pos;
	}

	Vec2i size (1, 1);
	size += brush.pos - oripos;
	size = size.LowerBound(Vec2i(1, 1));

	brush.grid.Resize(size.x, size.y);
	
	for (int dy = 0; dy < brush.grid.Rows(); ++dy)
	{
		int tile = tileimg->TileAtPoint(oripos.x, oripos.y + dy);

		for (int dx = 0; dx < brush.grid.Cols(); ++dx)
		{
			brush.grid.Put(dx, dy, tile + dx);
		}
	}
}

const wchar_t *PROPSTRS[Stage::PROPCT] =
{
	L"solid",
	L"upstairs",
	L"downstairs",
	L"water",
	L"flammable"
};

void AriaEd::TileScreen()
{
	look.x = 0;
	look.y = 0;

	const char *WIN_TITLE = "Tiles";
	SDL_WM_SetCaption(WIN_TITLE, WIN_TITLE);

	Vec2i oripos (-1, -1);

	while (screenf == &AriaEd::TileScreen)
	{
		SDL_Event event;
	
		while (SDL_PollEvent(&event))
		{
			Event(event);
		}

		// brush.pos.x = std::max<int>(0, brush.pos.x);
		// brush.pos.x = std::min<int>(brush.pos.x, 
		// 			tileimg->Width() - tileimg.TileWidth());
		// brush.pos.y = std::max<int>(0, brush.pos.y);
		// brush.pos.y = std::min<int>(brush.pos.y, 
		// 			tileimg->Height() - tileimg.TileHeight());
		tileimg->Clamp(brush.pos.x, brush.pos.y);

		int tile = tileimg->TileAtPoint(brush.pos.x, brush.pos.y);

		switch(brush.cmd)
		{
		case Brush::NONE:
			if (oripos.x >= 0 || oripos.y >= 0)
			{
				brush.cmd = brush.NONE;
				screenf = &AriaEd::StageScreen;
			}
			break;

		case Brush::GRAB:
			TileGrab(oripos);
			break;

		case Brush::PUT:
			if(brush.prop < 0)
				break;

			Stg.PropSet(tile, brush.prop);
			break;

		case Brush::ERASE:
			if(brush.prop < 0)
				break;

			Stg.PropUnset(tile, brush.prop);
			break;
			
		default:
			;
		}

		Cam.Pos(look.x, look.y);
		Cam.Out();

		Vid.Clear();
		text.Clear();//memset(txt, 0, sizeof(txt));
		
		Vid.Flip(false, false);

		for (float y = 0; 
		     y < tileimg->Height();
		     y += tileimg->TileHeight())
		{
			for (float x = 0; 
			     x < tileimg->Width();
			     x += tileimg->TileWidth())
			{
				int tile = tileimg->TileAtPoint(x, y);
					
				Vid.Pos(x, y);
				Vid.SpriteDraw(*tileimg, tile);

				if (brush.prop >= 0
				    && Stg.PropGet(tile, brush.prop))
				{
					Vid.SpriteDraw(*propimg, brush.prop);
				}
			}
		}

		if (brush.prop >= 0)
		{
			Vid.Pos(brush.pos.x, brush.pos.y);
			Vid.SpriteDraw(*propimg, brush.prop);

			text.Printf(LHELPC, HELPR, L"L: Paint %s",
					PROPSTRS[brush.prop]);
		}
		if (oripos.x >= 0 || oripos.y >= 0)
		{
			Vid.Pos(oripos.x, oripos.y);
			Vid.BoxDraw(brush.grid.Cols(), brush.grid.Rows());
		}
		
		text.Printf(RHELPC, HELPR, L"R: Copy pattern");
		TextDraw();

		Vid.DrawEnd();
	}
}

// standard % operator may be incorrect for n < 0
inline static int Mod(int n, int d)
{
	return (n % d + d) % d;
}

void AriaEd::StagePaint(Vec2i & oripos)
{
	if (oripos.x < 0 || oripos.y < 0)
	{
		oripos = brush.pos;
	}

	for (int dy = 0; dy < brush.grid.Rows(); ++dy)
	{
		for (int dx = 0; dx < brush.grid.Cols(); ++dx)
		{
			Vec2i putpos (brush.pos);
			putpos.x += dx;
			putpos.y += dy;

			if (putpos.x >= Stg.Cols()
			    || putpos.y >= Stg.Rows())
			{
				continue;
			}

			Vec2i diff (brush.pos - oripos);
			diff.x += dx;
			diff.y += dy;

			int tilen = Mod(diff.x, brush.grid.Cols());
			tilen += brush.grid.Cols() 
				* Mod(diff.y, brush.grid.Rows());

			Stg.Put(putpos.x, putpos.y, brush.grid.GetI(tilen));
		}
	}
}

void AriaEd::StageGrab(Vec2i & oripos)
{
	if (oripos.x < 0 || oripos.y < 0)
	{
		oripos = brush.pos;
	}
	
	Vec2i size (1, 1);
	size += brush.pos - oripos;
	size.x = std::max(1, size.x);
	size.y = std::max(1, size.y);

	brush.grid.Resize(size.x, size.y);

	for (int dy = 0; dy < brush.grid.Rows(); ++dy)
		for (int dx = 0; dx < brush.grid.Cols(); ++dx)
		{
			Vec2i pos (oripos.x + dx, oripos.y + dy);
			int tile = Stg.Get(pos.x, pos.y);
			brush.grid.Put(dx, dy, tile);
		}
}

void AriaEd::StageBrushDraw()
{
	for (int dy = 0; dy < brush.grid.Rows(); ++dy)
	{
		for (int dx = 0; dx < brush.grid.Cols(); ++dx)
		{
			int tile = brush.grid.Get(dx, dy);

			Vid.Pos(brush.pos.x + dx, 
				brush.pos.y + dy);
					
			Vid.SpriteDraw(*tileimg, tile);

			if (brush.prop >= 0
			    && Stg.PropGet(tile, brush.prop))
			{
				Vid.SpriteDraw(*propimg, brush.prop);
			}
		}
	}
}

void AriaEd::StageScreen()
{
	static Vec2i stglook(0, 0);

	look = stglook;

	const char *WIN_TITLE = "Stage";
	SDL_WM_SetCaption(WIN_TITLE, WIN_TITLE);

	Vec2i oripos (-1, -1);

	while (screenf == &AriaEd::StageScreen)
	{
		SDL_Event event;

		while (SDL_PollEvent(&event))
		{
			Event(event);
		}

		Vec2i brushmin (0, 0);
		Vec2i brushmax (Stg.Cols(), Stg.Rows());

		if (brush.cmd == brush.REPLACE)
		{
			brushmax.x -= brush.grid.Cols() - 1;
			brushmax.y -= brush.grid.Rows() - 1;
		}
		if (brush.cmd == brush.RESIZE)
		{
			brushmin.x = Vid.WIDTH - 1;
			brushmin.y = Vid.HEIGHT - 1;
		}

		brush.pos.x = std::max(brushmin.x, brush.pos.x);
		brush.pos.y = std::max(brushmin.y, brush.pos.y);

		if (brush.cmd != brush.RESIZE)
		{
			brush.pos.x = std::min(brush.pos.x, brushmax.x - 1);
			brush.pos.y = std::min(brush.pos.y, brushmax.y - 1);
		}

		Cue overcue = Cues.Get(brush.pos.x, brush.pos.y);

		switch (brush.cmd)
		{
		case Brush::NONE:
			oripos.x = -1;
			oripos.y = -1;
			break;

		case Brush::LFACE:
		case Brush::RFACE:
			if (brush.cue.IsValid())
			{
				brush.cue.Face(brush.cmd==brush.LFACE? -1 : 1);
			}
			else if (overcue.IsValid())
			{
				overcue.Face(brush.cmd==brush.LFACE ? -1 : 1);
				Cues.Put(brush.pos.x, brush.pos.y, overcue);
			}
			brush.cmd = brush.NONE;
			break;

		case Brush::GRAB:
			if (overcue.IsValid() && oripos.x < 0)
			{
				if (brush.cue.IsValid())
					Cues.Put(brush.pos.x, brush.pos.y, 
						brush.cue);
				else
					Cues.Erase(brush.pos.x, brush.pos.y);

				brush.cue = overcue;
				brush.cmd = brush.NONE;
			}
			else
			{
				StageGrab(oripos);
			}

			break;

		case Brush::PUT:
			if (brush.cue.IsValid())
			{
				Cues.Put(brush.pos.x, brush.pos.y, brush.cue);

				brush.cue = Cue();
				brush.cmd = brush.NONE;
			}
			else
			{
				StagePaint(oripos);
			}
			break;

		case Brush::ERASE:
			if (overcue.IsValid())
			{
				Cues.Erase(brush.pos.x, brush.pos.y);
				brush.cmd = brush.NONE;
			}
			else
			{
				Stg.Put(brush.pos.x, brush.pos.y, 0);
			}
			break;

		default:
			;
		}

		Cam.Pos(look.x, look.y);
		Cam.Out();

		Vid.Clear();
		text.Clear();//memset(txt, 0, sizeof(txt));
		
		Stg.Draw();

		if (brush.prop > Stage::NOPROP)
		{
			for (int y = Cam.AxMin(Y); 
			     y < Cam.AxMax(Y); 
			     ++y)
			{
				if (y >= Stg.Rows())
					break;
				if (y < 0)
					continue;

				for (int x = Cam.AxMin(X); 
				     x < Cam.AxMax(X); 
				     ++x)
				{
					if (x >= Stg.Cols())
						break;
					if (x < 0)
						continue;

					int tile = Stg.Get(x, y);

					if(Stg.PropGet(tile, brush.prop))
					{
						Vid.Pos(x, y);
						Vid.SpriteDraw(*propimg, 
							      brush.prop);
					}
				}
			}
		}

		if (brush.cmd == brush.GRAB)
		{
			Vid.Pos(oripos.x, oripos.y);
			Vid.BoxDraw(brush.grid.Cols(), brush.grid.Rows());
		}
		else if (!brush.cue.IsValid())
		{
			if (brush.cmd==brush.NONE || brush.cmd==brush.REPLACE)
				StageBrushDraw();
		}
		
		for (int r = 0; r < Cues.Rows(); ++r)
		{
			for (int c = 0; c < Cues.Cols(); ++c)
			{
				Cue cue = Cues.Get(c, r);
				Image *img = actimgs[cue.Num()];
				if (img)
				{
					Vid.Pos(c, r);
					Vid.Flip(cue.Dir() < 0, false);
					Vid.SpriteDraw(*img, 0);
				}
			}
		}
		Cues.Draw(*cueimg);

		if (brush.cmd == brush.REPLACE)
		{
			text.Printf(LHELPC, HELPR, L"L: Find & replace");
			text.Printf(RHELPC, HELPR, L"R: Cancel");
		}
		else if (brush.cmd == brush.RESIZE)
		{
			Vec2i newsize (brush.pos.x + 1, brush.pos.y + 1);
			Vid.Pos(0, 0);
			Vid.BoxDraw(newsize.x, newsize.y);
			text.Printf(INFOC, INFOR, L"%4i * %4i", 
					newsize.x, newsize.y);
			text.Printf(LHELPC, HELPR, L"L: Resize stage");
			text.Printf(RHELPC, HELPR, L"R: Cancel");
		}
		else 
		{
			if (overcue.IsValid())
			{
				Image *actimg = actimgs[overcue.Num()];
				if(actimg)
				{
					Vid.Pos(brush.pos.x, brush.pos.y);
					Vid.SpriteBoxDraw(*actimg);
				}
			}

			if (brush.cue.IsValid())
			{
				Image *actimg = actimgs[brush.cue.Num()];
				Vid.Pos(brush.pos.x, brush.pos.y);
				Vid.Flip(brush.cue.Dir() < 0, false);

				if(actimg)
				{
					Vid.SpriteDraw(*actimg, 0);
					Vid.SpriteBoxDraw(*actimg);
				}

				Vid.SpriteDraw(*cueimg, brush.cue.Num());

				text.Printf(CUEC, INFOR, L"Held: %i", 
						brush.cue.num);

				if (overcue.IsValid())
				{
					text.Printf(LHELPC, HELPR, 
							L"L: Overwrite cue");
					text.Printf(RHELPC, HELPR, 
							L"R: Exchange cues");
				}
				else
				{
					text.Printf(LHELPC, HELPR, 
							L"L: Place cue");
					text.Printf(RHELPC, HELPR, 
							L"R: Copy pattern");
				}
			}
			else
			{
				text.Printf(LHELPC, HELPR, L"L: Paint pattern");
				if (overcue.IsValid())
				{
					text.Printf(CUEC, INFOR, L"Over: %i", 
							overcue.num);
					text.Printf(RHELPC, HELPR, 
							L"R: Grab cue");
				}
				else
				{
					text.Printf(RHELPC, HELPR, 
							L"R: Copy pattern");
				}
			}

			text.Printf(TILEC, INFOR, L"(%4i, %4i)=%4i", 
					brush.pos.x, brush.pos.y, 
					Stg.Get(brush.pos.x, brush.pos.y));
		}
		TextDraw();

		Vid.DrawEnd();
	}

	stglook = look;
}

static const char *ACTIMGNAMES[Cue::CUECT] =
{
	// nil and static
	NULL, "door", "door", "door", 
	NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL,

	// player
	"player/stand", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 

	// enemy
	"enemy/ibox", "enemy/drifter", "enemy/walker", "enemy/flyer",
	"enemy/tosser", "enemy/rock", "enemy/boss1", NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 

	// item
	//"item/ammos", "item/ammol", "item/points", "item/pointm", 
	//"item/pointl", "item/pointxl", "item/pointxxl", "item/pointxxxl",
	//"item/bullet", "item/bubble", "item/cloud", "item/ball", "item/option",
	//"item/heal", "item/shield", "item/bomb",
	//"item/clear", "item/melee", NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 

	// boat
	"boat"
};

#include <iostream>

int AriaEd::Run(int argc, char *args[])
{
	enum
	{
		SUCCESS,
		VIDFAIL,
		DISKFAIL,
		IMGFAIL,
		STGFAIL, 
		ACTFAIL
	};
	
	int error = SUCCESS;

	if (argc < 2)
	{
		std::cout << "Usage: ariaed [-f] <stage_name>" << std::endl;
		std::cout << "Options:" << std::endl;
		std::cout << "-f\tFull screen" << std::endl;
		std::cout << "Required:" << std::endl;
		std::cout << "\tTileset image img/<stage_name>.png" <<std::endl;
		std::cout << "\tTileset info img/<stage_name>.info" <<std::endl;
		return error;
	}

	Vec2i res (640, 480);
	Uint32 flags = 0;
	for (int i = 1; i < argc; ++i)
	{
		switch (args[i][0])
		{
			case '-':
				switch (args[i][1])
				{
				case 'f':
					flags |= SDL_FULLSCREEN;
					res.x = res.y = 0;
					break;
				}
				break;

			default:
				stgname = args[i];
		}
	}

	SDL_Init(SDL_INIT_EVERYTHING);

	if (!Vid.Open(res.x, res.y, flags))
		error = VIDFAIL;
	else
	{
		Vid.WinResize(SDL_GetVideoSurface()->w,
				SDL_GetVideoSurface()->h, 
				Vid.SCREWASPECT);
		Vid.ResizeToWin();
	}

	if (!Dsk.Open(args[0]))
		error = DISKFAIL;

	fontimg = AllImgs.Get("font");
	propimg = AllImgs.Get("props");
	cueimg = AllImgs.Get("cues");
	if (!fontimg || !propimg || !cueimg)
		error = IMGFAIL;

	if (!Stg.Open(stgname))
		error = STGFAIL;

	std::string cuesname = stgname + "cue";
	Cues.Resize(Stg.Cols(), Stg.Rows());
	Cues.Open(cuesname);
	AllImgs.BatchGet(actimgs, Cue::CUECT, 0, ACTIMGNAMES);

	if (error == SUCCESS)
	{
		tileimg = AllImgs.Get(stgname);

		SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 
				    SDL_DEFAULT_REPEAT_INTERVAL); 
		
		screenf = &AriaEd::StageScreen;
		while (screenf)
			(this->*screenf)();
	}

	AllImgs.Clear();

	Vid.Close();
	Dsk.Close();
	SDL_Quit();
	return error;
}

int main(int argc, char *args[])
{
	AriaEd ariaed;

	int result = ariaed.Run(argc, args);

	return result;
}
